/*
 * Copyright 2009 BioTeam Inc
 * 
 * Licensed under License GNU LESSER GENERAL PUBLIC LICENSE version 3.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.gnu.org/copyleft/lesser.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.bioteam.appweb.ds;

/*
 * modified the code from jdk java.io.SequenceInputStream
 */

import java.io.IOException;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DSS3SequenceInputStream extends InputStream
{
	public static long segsize = 1024 * 1024 * 200;
	static Log logger = LogFactory.getLog(DSS3SequenceInputStream.class);

	InputStream in;
	int index;
	String currentEtag;
	MessageDigest currentMD;
	HttpMethod currentMethod;

	String prefix;
	int segnum;
	HttpClient client;

	public DSS3SequenceInputStream(String prefix, long size, HttpClient client) throws IOException
	{
		segnum = (int) Math.ceil((double) size / (double) segsize);
		this.prefix=prefix;
		this.client = client;
		index = 0;
		nextStream();
	}

	/**
	 * Continues reading in the next stream if an EOF is reached.
	 */
	final void nextStream() throws IOException
	{
		if (in != null)
		{
			in.close();
			currentMethod.releaseConnection();
			byte[] digestbyte = currentMD.digest();
			String hex = new String(Hex.encodeHex(digestbyte));
			if (currentEtag.startsWith("\""))
				hex = '"' + hex + '"';
			if (!currentEtag.equals(hex))
			{
				throw new IOException("md5 does not match");
			}
		}
		
		if (index >= segnum)
		{
			in = null;
			return;
		}
		StringBuffer sb = new StringBuffer();
		Formatter f = new Formatter(sb);
		f.format("%03d", index);
		f.flush();
		f.close();

		String key = prefix + sb.toString();
		logger.info("downloading ..."+key);
		currentMethod = new GetMethod("http://bioteamds.s3.amazonaws.com/" + key);
		// HttpMethod method = new GetMethod("http://localhost:8080/" + key);
		currentMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(5, false));
		try
		{
			currentMD = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e)
		{
			currentMethod.releaseConnection();
			throw new IOException(e.getMessage());
		}

		// Execute the method.
		int statusCode = client.executeMethod(currentMethod);
		if (statusCode != HttpStatus.SC_OK)
		{
			logger.warn("failed in downloading file " + currentMethod.getStatusLine());
			currentMethod.releaseConnection();
			throw new IOException(currentMethod.getStatusLine().toString());
		}
		currentEtag = currentMethod.getResponseHeader("ETag").getValue();

		in = new DigestInputStream(currentMethod.getResponseBodyAsStream(), currentMD);

		index++;

	}

	public int available() throws IOException
	{
		if (in == null)
		{
			return 0; // no way to signal EOF from available()
		}
		return in.available();
	}

	public int read() throws IOException
	{
		if (in == null)
		{
			return -1;
		}
		int c = in.read();
		if (c == -1)
		{
			nextStream();
			return read();
		}
		return c;
	}

	
	public int read(byte b[], int off, int len) throws IOException
	{
		if (in == null)
		{
			return -1;
		} else if (b == null)
		{
			throw new NullPointerException();
		} else if (off < 0 || len < 0 || len > b.length - off)
		{
			throw new IndexOutOfBoundsException();
		} else if (len == 0)
		{
			return 0;
		}

		int n = in.read(b, off, len);
		if (n <= 0)
		{
			nextStream();
			return read(b, off, len);
		}
		return n;
	}

	public void close() throws IOException
	{
		if (in!=null)
		{
			in.close();
			currentMethod.releaseConnection();
		}
	}
	

}
